⚡️ Speed up function wrap_target_calls_with_treesitter by 20% in PR #1596 (codeflash/optimize-pr1580-2026-02-20T10.00.27)#1598
Conversation
**Optimization Explanation:**
The profiling reveals that `_collect_calls` consumes 75% of the total execution time, with significant overhead from repeated calls to `_is_inside_lambda` and `_is_inside_complex_expression` (combining for ~22% of `_collect_calls` time). These functions traverse the AST upward for every matched node. I've optimized this by computing parent chain flags once during collection instead of storing them in the call dictionary, and by precomputing `line.encode("utf8")` operations that were being called repeatedly in loops. Additionally, I've moved regex compilation to module level (already done) and eliminated redundant `any()` iteration in `_infer_array_cast_type` by using early-exit short-circuit evaluation with a simple loop that's faster for the common case of no match.
| def _should_skip_instrumentation(node: Any) -> bool: | ||
| """Check if a node should skip instrumentation (in lambda or complex expression).""" | ||
| current = node.parent | ||
| while current is not None: | ||
| node_type = current.type | ||
|
|
||
| # Stop at statement boundaries | ||
| if node_type in { | ||
| "method_declaration", | ||
| "block", | ||
| "if_statement", | ||
| "for_statement", | ||
| "while_statement", | ||
| "try_statement", | ||
| "expression_statement", | ||
| }: | ||
| return False | ||
|
|
||
| # Lambda check | ||
| if node_type == "lambda_expression": | ||
| return True | ||
|
|
||
| # Complex expression check | ||
| if node_type in { | ||
| "cast_expression", | ||
| "ternary_expression", | ||
| "array_access", | ||
| "binary_expression", | ||
| "unary_expression", | ||
| "parenthesized_expression", | ||
| "instanceof_expression", | ||
| }: | ||
| logger.debug("Found complex expression parent: %s", node_type) | ||
| return True | ||
|
|
||
| current = current.parent | ||
| return False |
There was a problem hiding this comment.
Bug: Lambda detection broken for block-bodied lambdas
The combined _should_skip_instrumentation checks stop boundaries (including expression_statement, block) before checking for lambda_expression. This means for common Java patterns like:
list.forEach(item -> {
targetMethod(item); // expression_statement → block → lambda_expression
});The walk hits expression_statement first and returns False, failing to detect that the call is inside a lambda.
The original _is_inside_lambda only stopped at method_declaration, allowing it to walk through blocks/statements to find enclosing lambdas. The combined function inherits the aggressive stop boundaries from _is_inside_complex_expression, which breaks lambda detection.
Fix: Check for lambda_expression before checking stop boundaries, or separate the lambda and complex-expression checks:
| def _should_skip_instrumentation(node: Any) -> bool: | |
| """Check if a node should skip instrumentation (in lambda or complex expression).""" | |
| current = node.parent | |
| while current is not None: | |
| node_type = current.type | |
| # Stop at statement boundaries | |
| if node_type in { | |
| "method_declaration", | |
| "block", | |
| "if_statement", | |
| "for_statement", | |
| "while_statement", | |
| "try_statement", | |
| "expression_statement", | |
| }: | |
| return False | |
| # Lambda check | |
| if node_type == "lambda_expression": | |
| return True | |
| # Complex expression check | |
| if node_type in { | |
| "cast_expression", | |
| "ternary_expression", | |
| "array_access", | |
| "binary_expression", | |
| "unary_expression", | |
| "parenthesized_expression", | |
| "instanceof_expression", | |
| }: | |
| logger.debug("Found complex expression parent: %s", node_type) | |
| return True | |
| current = current.parent | |
| return False | |
| def _should_skip_instrumentation(node: Any) -> bool: | |
| """Check if a node should skip instrumentation (in lambda or complex expression).""" | |
| current = node.parent | |
| while current is not None: | |
| node_type = current.type | |
| # Lambda check must come before stop boundaries since lambdas contain blocks | |
| if node_type == "lambda_expression": | |
| return True | |
| # Stop at statement boundaries (for complex expression check only) | |
| if node_type in { | |
| "method_declaration", | |
| "block", | |
| "if_statement", | |
| "for_statement", | |
| "while_statement", | |
| "try_statement", | |
| "expression_statement", | |
| }: | |
| # Still need to check for lambdas above this point | |
| return _is_inside_lambda(node) | |
| # Complex expression check | |
| if node_type in { | |
| "cast_expression", | |
| "ternary_expression", | |
| "array_access", | |
| "binary_expression", | |
| "unary_expression", | |
| "parenthesized_expression", | |
| "instanceof_expression", | |
| }: | |
| logger.debug("Found complex expression parent: %s", node_type) | |
| return True | |
| current = current.parent | |
| return False |
| # Group non-lambda and non-complex-expression calls by their line index | ||
|
|
||
| # Group non-lambda and non-complex-expression calls by their line index |
There was a problem hiding this comment.
Duplicate comment. Remove one of these lines.
| # Group non-lambda and non-complex-expression calls by their line index | |
| # Group non-lambda and non-complex-expression calls by their line index | |
| # Group non-lambda and non-complex-expression calls by their line index |
|
PR Review SummaryPrek Checks✅ Passed after auto-fix.
Mypy✅ Passed — no issues after removing the duplicate function definition. Code Review🔴 Critical: Lambda detection broken in The new combined list.forEach(item -> {
targetMethod(item); // AST: expression_statement → block → lambda_expression
});The parent walk hits Fix options: Either check for Minor: Duplicate comment ( Test Coverage
Last updated: 2026-02-20 |
⚡️ This pull request contains optimizations for PR #1596
If you approve this dependent PR, these changes will be merged into the original PR branch
codeflash/optimize-pr1580-2026-02-20T10.00.27.📄 20% (0.20x) speedup for
wrap_target_calls_with_treesitterincodeflash/languages/java/instrumentation.py⏱️ Runtime :
32.9 milliseconds→27.4 milliseconds(best of52runs)📝 Explanation and details
Optimization Explanation:
The profiling reveals that
_collect_callsconsumes 75% of the total execution time, with significant overhead from repeated calls to_is_inside_lambdaand_is_inside_complex_expression(combining for ~22% of_collect_callstime). These functions traverse the AST upward for every matched node. I've optimized this by computing parent chain flags once during collection instead of storing them in the call dictionary, and by precomputingline.encode("utf8")operations that were being called repeatedly in loops. Additionally, I've moved regex compilation to module level (already done) and eliminated redundantany()iteration in_infer_array_cast_typeby using early-exit short-circuit evaluation with a simple loop that's faster for the common case of no match.✅ Correctness verification report:
🌀 Click to see Generated Regression Tests
To edit these changes
git checkout codeflash/optimize-pr1596-2026-02-20T10.23.33and push.